﻿using Linq2Rest.Implementations;
using Linq2Rest.Provider;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Reflection;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using Xinchen.SOA.Manager.Api.Contracts;
using Xinchen.Utils;
using Yuchen.Log.Utils;

namespace Xinchen.SOA.Client
{
    /// <summary>
    /// 服务代理
    /// </summary>
    /// <typeparam name="TInterface"></typeparam>
    public class ServiceProxy<TInterface> : RealProxy
    {
        static Dictionary<Type, string> _serviceTypeMap = new Dictionary<Type, string>();
        static ServiceProxy()
        {
            _serviceTypeMap.Add(typeof(IServiceHostingService), "ServiceHostingService");
            _serviceTypeMap.Add(typeof(IServiceServices), "ServiceService");
        }


        static Dictionary<string, ServiceInfo> _serviceInfos = new Dictionary<string, ServiceInfo>();
        static Dictionary<int, ServiceHostingInfo> _serviceHostingInfos = new Dictionary<int, ServiceHostingInfo>();
        string _serverUrl;
        string _controllerName;
        static JsonNetSerializerFactory _factory = new JsonNetSerializerFactory();
        const string _urlFormat = "http://{0}/{1}/{2}";

        ServiceHostingInfo GetServiceHosting(int id)
        {
            var hostings = _serviceHostingInfos;
            if (!hostings.Any())
            {
                lock (_serviceHostingInfos)
                {
                    hostings = _serviceHostingInfos;
                    if (!hostings.Any())
                    {
                        var logClient = LogWriter.GetInstance();
                        logClient.WriteLogFile(typeof(ServiceProxy<>).Name, "首次获取宿主信息");
                        var serviceHostingService = ServiceBuilder.BuildService<IServiceHostingService>(ServiceConsts.ServiceUrl, ServiceConsts.ServiceHostingServiceName);
                        _serviceHostingInfos = hostings = serviceHostingService.GetServiceHostings().ToDictionary(x => x.Id);
                        try
                        {
                            var th = new Thread(RefreshServiceHostingInfos);
                            th.IsBackground = true;
                            th.Start();
                            logClient.WriteLogFile(typeof(ServiceProxy<>).Name, "宿主缓存刷新器运行中");
                        }
                        catch (AggregateException aes)
                        {
                            throw new Exception("宿主缓存刷新出错", aes);
                        }
                    }
                }
            }
            return hostings.GetOrDefault(id);
        }

        ServiceInfo GetServiceInfo(string contractName)
        {
            var serviceInfos = _serviceInfos;
            if (!serviceInfos.Any())
            {
                lock (_serviceInfos)
                {
                    serviceInfos = _serviceInfos;
                    if (!serviceInfos.Any())
                    {
                        var logClient = LogWriter.GetInstance();
                        logClient.WriteLogFile(typeof(ServiceProxy<>).Name, "首次获取服务信息");
                        var serviceService = ServiceBuilder.BuildService<IServiceServices>();
                        _serviceInfos = serviceInfos = serviceService.GetServices().ToDictionary(x => x.ContractName);
                        try
                        {
                            var th = new Thread(RefreshServiceInfos);
                            th.IsBackground = true;
                            th.Start();
                            logClient.WriteLogFile(typeof(ServiceProxy<>).Name, "服务缓存刷新器已启动");
                        }
                        catch (AggregateException aes)
                        {
                            throw new Exception("服务缓存刷新出错", aes);
                        }
                    }
                }
            }
            return serviceInfos.GetOrDefault(contractName);
        }

        static void RefreshServiceHostingInfos()
        {
            var logClient = LogWriter.GetInstance();
            var serviceHostingService = ServiceBuilder.BuildService<IServiceHostingService>();
            while (true)
            {
                Thread.Sleep(30000);
                logClient.WriteLogFile(typeof(ServiceProxy<>).Name, "开始刷新宿主缓存");
                var apps = serviceHostingService.GetServiceHostings().ToDictionary(x => x.Id);
                Interlocked.Exchange(ref _serviceHostingInfos, apps);
                logClient.WriteLogFile(typeof(ServiceProxy<>).Name, "已刷新宿主缓存");
            }
        }
        static void RefreshServiceInfos()
        {
            var logClient = LogWriter.GetInstance();
            var serviceHostingService = ServiceBuilder.BuildService<IServiceServices>();
            while (true)
            {
                Thread.Sleep(30000);
                logClient.WriteLogFile(typeof(ServiceProxy<>).Name, "开始刷新服务缓存");
                var apps = serviceHostingService.GetServices().ToDictionary(x => x.ContractName);
                Interlocked.Exchange(ref _serviceInfos, apps);
                logClient.WriteLogFile(typeof(ServiceProxy<>).Name, "已刷新服务缓存");
            }
        }

        internal ServiceProxy(string serverUrl, string controllerName)
            : base(typeof(TInterface))
        {
            _serverUrl = serverUrl;
            _controllerName = controllerName;
        }
        internal ServiceProxy()
            : base(typeof(TInterface))
        {
        }
        public override System.Runtime.Remoting.Messaging.IMessage Invoke(System.Runtime.Remoting.Messaging.IMessage msg)
        {
            var logWriter = LogWriter.GetInstance();
            var address = string.Empty;
            IMethodCallMessage methodCallMessage = (IMethodCallMessage)msg;
            var method = (MethodInfo)methodCallMessage.MethodBase;
            string fullName = method.DeclaringType.FullName;
            try
            {
                logWriter.WriteLogFile(GetType().Name, "准备调用服务，契约：" + fullName + "，Action：" + method.Name);
                var serviceTypeName = _serviceTypeMap.GetOrDefault(method.DeclaringType);
                var url = ServiceConsts.ServiceUrl;
                if (string.IsNullOrWhiteSpace(serviceTypeName))
                {
                    var serviceInfo = GetServiceInfo(fullName);
                    if (serviceInfo == null)
                    {
                        logWriter.WriteLogFile(GetType().Name, "未找到服务：" + fullName);
                        throw new Exception("未找到服务：" + fullName);
                    }
                    serviceTypeName = serviceInfo.ServiceName.Split('.').LastOrDefault();
                    var serviceHosting = GetServiceHosting(serviceInfo.ServiceHostingId);
                    if (serviceHosting == null)
                    {
                        logWriter.WriteLogFile(GetType().Name, "未找到编号为" + serviceInfo.ServiceHostingId + "的宿主");
                        throw new Exception("未找到宿主：" + serviceInfo.ServiceHostingId);
                    }
                    url = serviceHosting.Address;
                }
                address = string.Format(_urlFormat, url, serviceTypeName, method.Name);
                var returnType = method.ReturnType;
                if (returnType.IsGenericType && typeof(IQueryable<>) == returnType.GetGenericTypeDefinition())
                {
                    logWriter.WriteLogFile(GetType().Name, "LINQ2Rest服务地址：" + address);
                    var uri = new Uri(address);
                    var jsonRestClient = new JsonNetRestClient(uri);
                    var type = typeof(RestContext<>).MakeGenericType(returnType.GetGenericArguments()[0]);
                    var client = Activator.CreateInstance(type, jsonRestClient, _factory);
                    var query = type.GetProperty("Query").GetValue(client);
                    return new ReturnMessage(query, new object[0], 0, methodCallMessage.LogicalCallContext, methodCallMessage);
                }
                else
                {
                    logWriter.WriteLogFile(GetType().Name, "服务地址：" + address);
                    var serviceType = method.DeclaringType;
                    var parameters = method.GetParameters();
                    HttpContent httpContent = null;
                    if (TypeHelper.IsValueType(parameters[0].ParameterType))
                    {
                        //多参数传递，每一个参数都是一个基类型
                        var formParameters = new List<KeyValuePair<string, string>>();
                        var parameterIndex = 0;
                        foreach (var parameter in parameters)
                        {
                            formParameters.Add(new KeyValuePair<string, string>(parameter.Name, Convert.ToString(methodCallMessage.InArgs[parameterIndex++])));
                        }
                        httpContent = new FormUrlEncodedContent(formParameters);
                    }
                    else
                    {
                        //只有一个参数
                        httpContent = new ObjectContent(parameters[0].ParameterType, methodCallMessage.InArgs[0], new JsonMediaTypeFormatter());
                    }
                    IMethodReturnMessage returnMessage = null;
                    var httpClient = new HttpClient();
                    HttpResponseMessage result = null;
                    using (httpClient)
                    {
                        result = httpClient.PostAsync(address, httpContent).Result;
                        if (!result.IsSuccessStatusCode)
                        {
                            var message = string.Format("请求地址为{0}的服务时发生错误:{1}", address, result.Content.ReadAsStringAsync().Result);
                            logWriter.WriteLogFile(GetType().Name, message);
                            throw new Exception(message);
                        }
                        else
                        {
                            logWriter.WriteLogFile(GetType().Name, "调用成功");
                        }
                    }
                    if (result != null)
                    {
                        if (TypeHelper.IsValueType(method.ReturnType))
                        {
                            var returnString = result.Content.ReadAsStringAsync().Result.TrimStart('"').TrimEnd('"');
                            var objValue = Convert.ChangeType(returnString, method.ReturnType);
                            returnMessage = new ReturnMessage(objValue, new object[0], 0, methodCallMessage.LogicalCallContext, methodCallMessage);
                        }
                        else
                        {
                            var formatter = new JsonMediaTypeFormatter();
                            var obj = formatter.ReadFromStream(method.ReturnType, result.Content.ReadAsStreamAsync().Result, Encoding.UTF8, null);
                            returnMessage = new ReturnMessage(obj, new object[0], 0, methodCallMessage.LogicalCallContext, methodCallMessage);

                        }
                    }
                    return returnMessage;
                }
            }
            catch (AggregateException ae)
            {
                var message = string.Format("请求地址为{0}的服务时发生错误:{1}", address, ae.Message);
                logWriter.WriteLogFile(GetType().Name, message);
                foreach (var item in ae.InnerExceptions)
                {
                    message = string.Format("请求地址为{0}的服务时发生错误:{1}", address, item.ToString());
                    logWriter.WriteLogFile(GetType().Name, message);
                }
                return new ReturnMessage(ae, methodCallMessage);
            }
            catch (HttpRequestException hre)
            {
                var message = string.Format("请求地址为{0}的服务时发生错误:{1}", address, hre.ToString());
                logWriter.WriteLogFile(GetType().Name, message);
                return new ReturnMessage(hre, methodCallMessage);
            }
            catch (WebException we)
            {
                using (var sr = new StreamReader(we.Response.GetResponseStream()))
                {
                    var message = string.Format("请求地址为{0}的服务时发生错误:{1}", address, sr.ReadToEnd());
                    logWriter.WriteLogFile(GetType().Name, message);
                    return new ReturnMessage(we, methodCallMessage);
                }
            }
            catch (Exception ex)
            {
                var message = string.Format("请求地址为{0}的服务时发生错误:{1}", address, ex.ToString());
                logWriter.WriteLogFile(GetType().Name, message);
                return new ReturnMessage(ex, methodCallMessage);
            }
        }
    }
}
